﻿package com.calefk.modules.sysfx {
	
	import com.calefk.core.ClassBase;
	import com.calefk.core.List;
	import com.calefk.modules.math.Vector2D;
	import com.calefk.util.Generic;
	import com.calefk.util.TimerCustom;
	import flash.display.MovieClip;
	
	 
	public class Emitter extends ClassBase {
		
		private var mCanvas:MovieClip;
		
		private var mCountParticle:int = 0;
		private var mMaxInScene:int;
		private var mTotalParticles:int;
		
		// position origin and angle
		private var mOriginX:Number;
		private var mOriginY:Number;
		private var mDirectionOrigin:Vector2D;
		private var mAngleOriginRad:Number;
		
		// flags rotation
		private var mApplyRotVelocity:Boolean;
		private var mApplyRotAcceleration:Boolean;
		
		private var mAmplitudeGenerate:Number;
		
		// vars to generate velocity
		private var mMinAngleGenerateV:int;
		private var mMaxAngleGenerateV:int;
		private var mMinSpeedV:Number;
		private var mMaxSpeedV:Number;
		
		// vars to acceleration
		private var mAngleAcceleration:Number;
		private var mSpeedAcceleration:Number;
		
		private var mMountTimeGenerate:Number;
		
		// vars to particle
		private var mSkinParticle:String;
		private var mShrink:Number;
		private var mFriction:Number;
		private var mFadeReduce:Number;
		private var mTimeLife:Number;
		private var mSpinSpeed:Number;
		private var mSpinWithVelocity:Boolean;
		
		private var mTimerGenerate:TimerCustom;
		private var mListParticles:List;
		
		private var mTimeDt:Number = 0;
		
		private static const ST_START    :int = 0;
		private static const ST_STOP     :int = 1;
		private static const ST_FINALIZE :int = 2;
		private var state:int;
		
		/** Permite rotar el origen del emisor en radianes,
		 * su equivalente en grados seria 0 a 180 horario y
		 * de -0 a -180 antihorario
		 */
		public function set rotation(radians:Number):void {
			var x0:Number = Math.cos(radians);
			var y0:Number = Math.sin(radians);
			mDirectionOrigin.x = x0;
			mDirectionOrigin.y = y0;
			mAngleOriginRad = radians;
		}
		
		/** Obtiene o asigna la rotacion del origen en radianes
		 * su equivalente en grados seria 0 a 180 horario y de -0 a -180 antihorario
		 */
		public function get rotation():Number {
			return mAngleOriginRad;
		}
		
		public function set originX(value:Number):void {
			mOriginX = value;
		}
		
		public function set originY(value:Number):void {
			mOriginY = value;
		}
		
		/** Obtiene o asigan la posicion en el eje X
		 */
		public function get origenX():Number {
			return mOriginX;
		}
		
		/** Obtiene o asigan la posicion en el eje X
		 */
		public function get origenY():Number {
			return mOriginY;
		}
        
        /** Devuelve el numero de particulas aun vivas
         */
        public function get countParticles():int {
            return mListParticles.size();
        }
		
		public function Emitter(canvas:MovieClip ,
				origenX:Number,
				origenY:Number,
                angleOrigin:Number,
				timeDt:int,
				totalParticles:int,
				maxInScene:int,
				mountTimeGenerate:int,
				amplitudeGenerate:Number,
				minAngleGenerateV:Number,
				maxAngleGenerateV:Number,
				minSpeedV:Number,
				maxSpeedV:Number,
				angleAcceleration:Number,
				speedAcceleration:Number,
				skinParticle:String,
				shrink:Number,
				friction:Number,
				fadeReduce:Number,
				timeLife:Number,
				spinSpeed:Number,
				spinWithVelocity:Boolean,
				applyRotVelocity:Boolean,
				applyRotAcceleration:Boolean
			) {
			mCanvas = canvas;
			mOriginX = origenX;
			mOriginY = origenY;
			mTimeDt = timeDt;
			mTotalParticles = totalParticles;
			mMaxInScene = maxInScene;
			mMountTimeGenerate = mountTimeGenerate;
			mAmplitudeGenerate = amplitudeGenerate;
			mMinAngleGenerateV = minAngleGenerateV;
			mMaxAngleGenerateV = maxAngleGenerateV;
			mMinSpeedV = minSpeedV;
			mMaxSpeedV = maxSpeedV;
			mAngleAcceleration = angleAcceleration;
			mSpeedAcceleration = speedAcceleration;
			mSkinParticle = skinParticle;
			mShrink = shrink;
			mFriction = friction;
			mFadeReduce = fadeReduce;
			mTimeLife = timeLife;
			mSpinSpeed = spinSpeed;
			mSpinWithVelocity = spinWithVelocity;
			mApplyRotVelocity = applyRotVelocity;
			mApplyRotAcceleration = applyRotAcceleration;
			
			// init objects
			mListParticles = new List();
			
			mTimerGenerate = new TimerCustom(validateCreateParticle, 0 , mMountTimeGenerate);
			mTimerGenerate.start();
            
			// cero grados
			mDirectionOrigin = new Vector2D(1, 0); 
			mDirectionOrigin.normalize();
            
            // rotando la direccion del origen
            Vector2D.rotateRevSameVector(mDirectionOrigin , Generic.degreesToRad(angleOrigin));
			
			mAngleOriginRad = mDirectionOrigin.getRad();
            
			// init states
			start();
		}
		
		override public function update(dt:int):void {
			updateInternal(mTimeDt);
		}
		
		private function updateInternal(dt:int):void {
			switch (state) {
				case ST_START:
				case ST_STOP:
					updateObjects(dt);
					break;
				case ST_FINALIZE:
					updateObjects(dt);
					// Si es cero el numero de particulas , el sistema
					// eliminara al emisor
					if (mListParticles.size() == 0) {
						toClean = true;
					}
					break;
			}
		}
		
		private function updateObjects(dt:int):void {
			mTimerGenerate.update(dt);
			mListParticles.update(dt);	
		}
		
		
		private function validateCreateParticle():void {
			// verifica si llego al total de particulas a generar
			if (mTotalParticles > 0 && mCountParticle == mTotalParticles) {
				mTimerGenerate.stop();
				mTimerGenerate.clean();
			} else {
				// Fuerza la eliminacion del primer elemento para poder seguir generando particulas
				if (mListParticles.size() == mMaxInScene) {
					mListParticles.first().marketToClean();
				}
				// crea nueva particula
				mListParticles.add(getNewParticle());
				mCountParticle++;
			}
		}
		
		private function getNewParticle():Particle {
			/*
			 * Para generar la velocidad primero se genera por random la cantidad
			 * de grados a girar (sentido antihorario de 0 a 360 positivo), luego 
			 * se inicializa con un angulo de 0 grados, en caso de que se quiera 
			 * rotar la velocidad en base al angulo de origen la velocidad 
			 * se reemplazara por el vector de origen, por ultimo se escala y se gira.
			 * El mismo mecanismo es para la aceleracion.
			 * */
			
			// Velocidad
			var rangle:int = Generic.random(mMinAngleGenerateV , 
					mMaxAngleGenerateV);
			var v:Vector2D = new Vector2D(1 , 0);
			if (mApplyRotVelocity) {
				v.replaceValuesWith(mDirectionOrigin);
			}
			v.scale(Generic.randomDecimal(mMinSpeedV, mMaxSpeedV));
			Vector2D.rotateRevSameVector(v , Generic.degreesToRad(rangle));
			
			// Aceleration
			var a:Vector2D = new Vector2D(1 , 0);
			if (mApplyRotAcceleration) {
				a.replaceValuesWith(mDirectionOrigin);
			}
			a.scale(mSpeedAcceleration);
			Vector2D.rotateRevSameVector(a , Generic.degreesToRad(mAngleAcceleration));
			
			// Position
			var vamplitude:Vector2D = mDirectionOrigin.getPerp();
			vamplitude.scale(Generic.randomDecimal(-mAmplitudeGenerate , mAmplitudeGenerate));
			
			var px:Number = mOriginX + vamplitude.x;
			var py:Number = mOriginY + vamplitude.y;
			
			var particle:Particle = new Particle(mCanvas, mSkinParticle, px, py , a , v ,mShrink , mFriction , mFadeReduce , mTimeLife, mSpinSpeed , mSpinWithVelocity);  
			return particle;
		}
        
		/** Desativa la generacion de particulas
		 */
		public function stop():void {
			state = ST_STOP;
			mTimerGenerate.stop();
		}
				
        /** Activa la generacion de particulas
		 */
		public function start():void {
			state = ST_START;
			mTimerGenerate.start();
		}
        
		/** Desativa la generacion de particulas , y espera a las particulas vivas mueran
         * este metodo solo debe ser usado con SystemFX
		 */
		public function finalize():void {
			state = ST_FINALIZE;
			mTimerGenerate.stop();
		}
        
		/** Limpia todas las partilas del emisor, 
         *  este metodo solo debe ser usado con SystemFx
		 */
		override public function clean():void {
			mTimerGenerate.clean();
			mListParticles.clean();
			
			mTimerGenerate = null;
			mListParticles = null;
			
			mDirectionOrigin = null;
			mCanvas = null;
			mSkinParticle = null;
		}
	}
}